var origFs 		= require("fs");
var utils  		= require("../../preprocessing/src/utils");
var pathOrig 	= require("path");

var fs = ( function() {

    var Local = ( function() {

	    function Local(localpath) {
	    	this.mFileCache = {};
	    	if (localpath) {
	    		this.mLocalPath = localpath;
	    	}
	    }

	    Local.prototype.exists = function(path) {
	    	return this.existsSync(path);
	    }

	    Local.prototype.existsSync = function(path) {
	    	if (path in this.mFileCache) {
	    		return !!this.mFileCache[path];
	    	}
	    	return this.mLocalPath && origFs.existsSync(this.mLocalPath+path);
	    }

	    Local.prototype.read = function(path) {
	    	return this.readSync(path);
	    }

	    Local.prototype.name = function() {
	    	return "Local";
	    }

	    Local.prototype.readSync = function(path) {
	    	var notFoundError = new Error("Path "+path+" not found at FileSystem '"+this.constructor.name+"'");
	    	notFoundError.notFound = true;
	    	if (path in this.mFileCache) {
	    		if (this.mFileCache[path]) {
	    			return this.mFileCache[path];
	    		}
	    		throw notFoundError;
	    	}
	    	else if (this.mLocalPath && origFs.existsSync(this.mLocalPath+path)) {
	    		return origFs.readFileSync(this.mLocalPath+path);
	    	}
	    	throw notFoundError;
	    }

	    Local.prototype.writeSync = function(path, data) {
	    	this.mFileCache[path] = data;
	    }

	    Local.prototype.write = function(path, data) {
	    	this.writeSync(path, data);
	    }

	    Local.prototype.delete = function(path) {
	    	for (var storageEntryKey in this.mFileCache) {
	    		var matches = true;
	    		for (var i = 0; i < path.length; i++) {
	    			if (path[i] != storageEntryKey[i]) {
	    				matches = false;
	    			}
	    		}
	    		if (matches) {
	    			this.mFileCache[storageEntryKey] = null;
	    		}
	    	}
	    }

	    Local.prototype.save = function(path) {
	    	if (this.mLocalPath) {
	    		utils.copyFolder();
	    	}
	    	for (var storageEntryKey in this.mFileCache) {
	    		var dstFile = path+storageEntryKey;
	    		utils.mkdirRecursive(pathOrig.dirname(dstFile));
	    		origFs.writeFileSync(
	    			dstFile,
	    			JSON.stringify(this.mFileCache[storageEntryKey], undefined, 4));
	    	}
	    }

	    Local.prototype.modifiedFiles = function(path) {
	    	return this.mFileCache;
	    }

	    return Local;
	})();

	var Remote = (function() {

	    function Remote(jsonfile) {
	    	Local.call(this, jsonfile);
	    }
    	Remote.prototype.__proto__ = Local.prototype;

	    Remote.prototype.readFolder = function(path) {
	    	throw new Error("Not implemented");
	    }

	    Remote.prototype.name = function() {
	    	return "Remote";
	    }

	    Remote.prototype.exists = function(path) {
	    	return $.Deferred().resolve(Local.prototype.exists.call(this, path)).promise();
	    }

	    Remote.prototype.read = function(path) {
	    	try {
	    		return $.Deferred().resolve(Local.prototype.read.call(this, path)).promise();
	    	} catch (e) {
	    		return $.Deferred().reject(e).promise();
	    	}
	    }

	    Remote.prototype.mkdir = function(path) {
	    	return $.Deferred().resolve().promise();
	    }


	    Remote.prototype.delete = function(path) {
	    	return $.Deferred().resolve(Local.prototype.delete.call(this, path)).promise();
	    }


	    Remote.prototype.write = function(path, data) {
	    	return $.Deferred().resolve(Local.prototype.write.call(this, path, data)).promise();
	    }

	    return Remote;
	})();

	var Global = ( function() {

	    function Global(rootUrl) {
	    	this.mRootUrl = rootUrl;
	    }

	    Global.prototype.name = function() {
	    	return "Global";
	    }

	    Global.prototype.read = function(path) {
	    	var path = this.mRootUrl+"/"+path;
	    	var notFoundError = new Error("Path "+path+" not found at FileSystem 'Global'");
	    	if (origFs.existsSync(path)) {
	    		return origFs.readFileSync(path);
	    	}
	    	return $.Deferred().reject(notFoundError).promise();
	    }

	    return Global;
	})();


	return {
		Local  : Local,
		Remote : Remote,
		Global : Global
	}
})();

module.exports = fs;

